package components.resultLayer {
	import UIFree.ObjectEvent;
	import UIFree.uif_bitmap;
	import UIFree.uif_ecomponent;
	
	import UIUtils.uif_color;
	import UIUtils.uif_matrix;
	import UIUtils.uif_util;
	
	import classes.clt_events;
	
	import components.clt_component;
	import components.clt_workspace;
	
	import data.cd_utils;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.PixelSnapping;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.filters.GlowFilter;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.TextField;
	import flash.text.TextFormatAlign;

	public class clt_resultBitmap extends uif_bitmap {
		private static const FACENAME: String 	= 'face';
		private static const CLOTHNAME: String 	= 'cloth';
		private static const HAIRNAME: String 	= 'hair';
		private static const DEFNAMES: Array	= [FACENAME, CLOTHNAME, HAIRNAME];
		
//		private var _face			: clt_elemBitmap;
		private var _elems			: Object;
		private var _elemNames		: Array;
		private var _background		: Bitmap;
		private var _autocorrection	: Boolean;
		private var _manualCorr		: Array;
		private var _standartCorr	: Array;
		private var _linkText		: TextField;
		private var _wsInfo			: Object;
		
//		private var _hairInfo	: Object;
//		private var _clothInfo	: Object;
		private var _maskInfo	: Object;
		private var _app		: clt_clothingBase;
		
		private static const _defPercent: Object = {
			hair: new Point(0.2, 0.5),
			cloth: new Point(0.4, -0.15)
		};
		
		public function clt_resultBitmap(parent: uif_ecomponent, bitmapData: BitmapData=null) {
			super(bitmapData);
			_manualCorr 	= [0,0,0];
			_standartCorr 	= [0,0];
			resetDefaultNames();
			_elems = {
				face	: null,
				cloth	: null,
				hair	: null
			};
			parent.addChild(this);
			_app = parent.app as clt_clothingBase;
			workspace.addEventListener(clt_events.GETRESULTBMP, doGetResult);
			workspace.addEventListener(clt_events.GETRESULTBITMAPS, doGetResultList);
			workspace.addEventListener(clt_events.DISPLAYREFRESH, doRefresh);
			workspace.addEventListener(clt_events.SETELEMENT, doAddElement);
			workspace.addEventListener(clt_events.UPDATEWORKSPACE, doUpdateWorkspace);
			workspace.addEventListener(clt_events.UPDATEFACE, doUpdateFace);
			workspace.addEventListener(clt_events.GETELEMS, doGetElems);
		}
		
		private function resetDefaultNames(): void {
			_elemNames	= uif_util.cloneArr(DEFNAMES);
		}
		
		public function get _face(): clt_elemBitmap {
			return _elems[FACENAME];
		}
		
		protected function initLinkText(): void {
			_linkText = new TextField();
			uif_util.applyTextFormat(_linkText, {
				font	: 'arial',
				size	: 52,
				color	: 0,
				align	: TextFormatAlign.CENTER
			});
			_linkText.filters = [
				new GlowFilter(0xFFFFFF, 1, 5, 5, 3, 2)
			];
		}
		
		public function get clt(): clt_clothingBase {
			return (parent as clt_component).app as clt_clothingBase;
		}
		
		public function get autocorrection(): Boolean {
			return _autocorrection;
		}
		
		public function set autocorrection(a_set: Boolean): void {
			_autocorrection = a_set;
			refresh();
		}
		
		public function get manualCorr(): Array {
			return _manualCorr;
		}
		
		public function set manualCorr(a_corr: Array): void {
			_manualCorr = a_corr;
			refresh();
		}
		
		public function get standartCorr(): Array {
			return _standartCorr;
		}
		
		public function set standartCorr(a_corr: Array): void {
			_standartCorr = a_corr;
			refresh();
		}
		
		public function get maskInfo(): Object {
			return _maskInfo;
		}
		
		protected function get workspace(): clt_workspace {
			return _app.workspace;
		}
		
		protected function doUpdateWorkspace(e: Event): void {
			_wsInfo = uif_util.clone(workspace.info);
			refreshElems();
		} 
		
		protected function calcEars(percent: Point, a_elemRect: Rectangle): Array {
			return [
				a_elemRect.x + a_elemRect.width * percent.x,
				a_elemRect.y + a_elemRect.width * percent.y,
				a_elemRect.x + a_elemRect.width * (1 - percent.x),
				a_elemRect.y + a_elemRect.width * percent.y
			];
		}
		
		protected function doAddElement(e: ObjectEvent): void {
			setElement(e.object.elemClass, e.object.name, e.object.bmp, e.object.data);
		}
		
		protected function setElement(eClass: Class, elemName: String, bmp: Bitmap, elemInfo: Object, toFront: Boolean = true): void {
			if (_elemNames.indexOf(elemName) == -1)
				_elemNames.splice(toFront?_elemNames.length:0, 0, elemName);
			if (_elems.hasOwnProperty(elemName) && _elems[elemName]) {
				_elems[elemName].bitmapData.dispose();
				if (!bmp) {
					var index: int = _elemNames.indexOf(elemName);
					_elemNames.splice(_elemNames.indexOf(elemName), 1);
					delete(_elems[elemName]);
				}
			}
			if (bmp) {
				eClass 					= eClass?eClass:clt_elemBitmap;
				_elems[elemName] 		= new eClass(elemName, bmp.bitmapData.clone(), elemInfo);
				if (!_elems[elemName]._data.ears && _defPercent.hasOwnProperty(elemName))
					_elems[elemName]._data.ears = calcEars(_defPercent[elemName], _elems[elemName].bitmapData.rect);
				
				if (_face) refreshElem(_elems[elemName]);
				traceUpdateElement(_elems[elemName]._data);
			} else _app.workspace.dispatchEvent(new ObjectEvent(clt_events.UPDATEELEMENT, {
				name: elemName,
				data: null	
			}, true));
			refresh();
		}
		
		private function doUpdateFace(e: ObjectEvent): void {
			updateFace(e.object as BitmapData);
		}
		
		private function doGetElems(e: ObjectEvent): void {
			e.object.result = _elems;
		}
		
		private function updateFace(a_face: BitmapData): void {
			if (_face) {
				_face.bitmapData.dispose();
				_face.bitmapData = a_face.clone();
				refresh();
			} else {
				_elems[FACENAME] = new clt_elemBitmap(FACENAME, a_face.clone(), {});
				updateCutout(_maskInfo);
			}
			
			_face.smoothing = true;			
			refreshElems();
		}
		
		public function get face(): Bitmap {
			return _face;
		}
		
		protected function refreshElems(): void {
			for each (var elem: String in _elemNames) refreshElem(_elems[elem]);
		}
		
		protected function refreshElem(elem: clt_elemBitmap): void {
			if (elem && _maskInfo) {
				elem.transform.matrix = elem.getMatrix(_maskInfo, getFaceRect(), scaleFromMask(bitmapData));
				if (elem.name == FACENAME) elem.colorCorrect = colorCorrection();
			}
		}
		
		public function updateCutout(a_maskInfo: Object): void {
			_maskInfo = a_maskInfo;
			if (_face && _maskInfo) _face._data.ears = _maskInfo.ears;
			refresh();
		}
		
		public function updateBackground(a_background: Bitmap): void {
			if (_background) _background.bitmapData.dispose();
			_background = a_background;
			refresh();
		}
		
		public function forEach(proc: Function): void {
			for each (var i: String in _elemNames) proc(getElem(i), i);
		}
		
		public function getElem(elemName: String): clt_elemBitmap {
			return _elems[elemName];
		}
		
		
/*		protected function calcMatrix(ears1: Array, ears2: Array): Matrix {
			var scale	: Number = ears1[1].subtract(ears1[0]).length/ears2[1].subtract(ears2[0]).length;
			var mat		: Matrix = new Matrix();
			mat.translate(-ears2[0].x, -ears2[0].y);
			mat.scale(scale, scale);
			mat.translate(ears1[0].x, ears1[0].y);
			return mat;
		}
*/
		public function scaleFromMask(a_bitmapData: BitmapData=null): Number {
			a_bitmapData = a_bitmapData?a_bitmapData:bitmapData;
			return (a_bitmapData.width * workspace.info.face.percent) / (_maskInfo.ears[2] - _maskInfo.ears[0]);
		}
		
		public function resetElemEars(elem: clt_elemBitmap, offset: Point=null, scale: Number=1): void {
			if (elem.name == FACENAME) {
				if (offset) {
					_wsInfo.face.pos[0] -= offset.x/elem.bitmapData.width;	
					_wsInfo.face.pos[1] -= offset.y/elem.bitmapData.height;
				} else _wsInfo.face.percent *= scale;
			} else {
				elem.updateTransform(this, offset, scale);
			}
			refresh();
		}
		
		public function traceUpdateElement(elem: Object): void {
			for each (var i: String in _elemNames)
				if (_elems[i] && (_elems[i]._data == elem)) {
					_app.workspace.dispatchEvent(new ObjectEvent(clt_events.UPDATEELEMENT, {
						name: i,
						data: elem	
					}, true));			
					break;
				}
		}
		
		public function paintToBitmap(a_bitmapData: BitmapData, options: Object): void {
			repaintBitmapData(a_bitmapData, options);
		}
		
		public function getFaceRect(a_bitmapData: BitmapData=null): Rectangle {
			if (!a_bitmapData) a_bitmapData = bitmapData;
			return faceMatrixCalc(a_bitmapData).transformRect(_face.bitmapData.rect);
		}
		
		protected function colorCorrection(level: Number=0x8F): ColorTransform {
			
			function copyFragment(source: DisplayObject, p: Point, size: Number, blur: Number = 0): Bitmap {
				var bmp: uif_bitmap = new uif_bitmap(new BitmapData(size, size));
				var mat: Matrix = new Matrix(1, 0, 0, 1, -(p.x - size / 2), -(p.y - size / 2));
				bmp.bitmapData.draw(source, mat);
				if (blur) bmp.bitmapData.applyFilter(bmp.bitmapData, bmp.bitmapData.rect, new Point(0, 0), new BlurFilter(blur, blur)); 
				return bmp;
			}
			
			function averageColor(image: DisplayObject, corr: Array, size: Number): Array {
				var colors: Array = new Array();
				for (var i:int=0; i<corr.length; i+=2) {
					var bmp: Bitmap = copyFragment(image, new Point(corr[i], corr[i + 1]), size, size);
					colors.push(uif_color.uintToRGBA(bmp.bitmapData.getPixel(size / 2, size / 2))); 
					bmp.bitmapData.dispose();
				}
				
				var summ: Array = [0,0,0];
				colors.forEach(function(color: Array, index: int, arr: Array): void {
					summ[0] += color[1];
					summ[1] += color[2];
					summ[2] += color[3];
				});
				summ[0] /= colors.length;
				summ[1] /= colors.length;
				summ[2] /= colors.length;
				return summ;
			}
			
			var br		: Number = _standartCorr[0]/100;
			var con		: Number = (100 + _standartCorr[1])/100;
			var offset	: Number = 0x7F * (1 - con) + 0xFF * br;
			
			var ct: ColorTransform = new ColorTransform(
				con,
				con,
				con,
				1,
				offset,
				offset,
				offset
			);
			if (autocorrection) {
				if (_maskInfo.hasOwnProperty('corr') && 
					_elems[CLOTHNAME] &&
					_elems[CLOTHNAME]._data.corr) {
					var calibrateColor: Array = averageColor(_elems[CLOTHNAME], _elems[CLOTHNAME]._data.corr, 10);
					var corrColor: Array = averageColor(_face, _maskInfo.corr, 10);
					var correct: Array = [
						(calibrateColor[0] - corrColor[0]) / 0xFF, 
						(calibrateColor[1] - corrColor[1]) / 0xFF, 
						(calibrateColor[2] - corrColor[2]) / 0xFF 
					];
					ct.redOffset 	+= correct[0] * level;
					ct.greenOffset 	+= correct[1] * level;
					ct.blueOffset 	+= correct[2] * level;
				}
			} else {
				ct.redOffset 	+= manualCorr[0];
				ct.greenOffset 	+= manualCorr[1];
				ct.blueOffset 	+= manualCorr[2];
			}
			return ct;
		}
		
		private function paintBackgroud(a_bitmapData: BitmapData, scale: Number=1): void {
			var bmat		: uif_matrix = new uif_matrix();
			bmat.scale(scale, scale);
			bmat.translate((a_bitmapData.rect.width - _background.bitmapData.width * scale) / 2,
				(a_bitmapData.rect.height - _background.bitmapData.height * scale) / 2);
			_background.smoothing = true;
			a_bitmapData.draw(_background, bmat);			
		}
		
		private function paintLinkText(a_bitmapData: BitmapData, scale: Number=1): void {
			if (!_linkText) initLinkText();
			
			_linkText.text 	 = clt.sitedata.TEXTFROMRESULT;
			var tSize: Point = new Point(_linkText.textWidth * 1.15, _linkText.textHeight * 1.2);
			
			_linkText.width  = tSize.x;
			_linkText.height = tSize.y;
			
			var tm: Matrix 			= new Matrix();
			var inRect: Rectangle	= linkTextRect(a_bitmapData.rect);
			var rect: Rectangle 	= uif_util.EnterHere(inRect, tSize);
			scale	= rect.width / tSize.x;
			
			tm.scale(scale, scale);
			tm.translate(rect.x, rect.y);
			a_bitmapData.draw(_linkText, tm, new ColorTransform(1, 1, 1, 0.5));			
		}
		
		protected function repaintBitmapData(a_bitmapData: BitmapData, options: Object): void {
			if (_face && _maskInfo) {
				var scale		: Number = 1;
				
				if (_background) {
					scale = Math.max(a_bitmapData.rect.width/ _background.bitmapData.width,
						a_bitmapData.rect.height/ _background.bitmapData.height);
					if (!options || !options.noback)
						paintBackgroud(a_bitmapData, scale);
				}
				
				var _faceRect	: Rectangle = getFaceRect(a_bitmapData)
				for each (var i: String in _elemNames) 
					if (_elems[i]) {
						if (_elems[i].isNoEars) _elems[i].drawAsScale(a_bitmapData, a_bitmapData.rect.width / _elems[i].width);
						else _elems[i].draw(a_bitmapData, workspace.info.face.percent, _maskInfo, _faceRect);
					}
					
				if (clt.sitedata.TEXTFROMRESULT && 
					!clt.user.noLinkText && (!options || !options.noLinkText))
					paintLinkText(a_bitmapData, scale);
				
				if (!(options && options.resultOnly))
					clt.workspace.dispatchEvent(new ObjectEvent(clt_events.AFTERDRAWRESULT, a_bitmapData));
			} 			
		}
		
		protected function bitmapsList(depthMax: Number, a_size: Point): Array {
			var result: Array;
			if (_face && _maskInfo && _background) {
				result = new Array();
				function createBitmap(level: Number=0): BitmapData {
					var bmd: BitmapData = new BitmapData(a_size.x, a_size.y, true, 0x00FFFFFF);
					var bmp: Bitmap = new Bitmap(bmd, PixelSnapping.AUTO, true);
					bmp.z = level * depthMax;
					result.push(bmp);
					return bmd; 
				}
				var scale: Number = Math.max(a_size.x/_background.bitmapData.width, a_size.y/_background.bitmapData.height);				
				paintBackgroud(createBitmap(1), scale);
				
				var a_bitmapData 	: BitmapData = createBitmap(0);
				var _faceRect	: Rectangle = getFaceRect(a_bitmapData)
					
				for each (var i: String in _elemNames) 
				if (_elems[i]) {
					if (_elems[i].isNoEars) _elems[i].drawAsScale(createBitmap(-1), a_size.x / _elems[i].width);
					else _elems[i].draw(a_bitmapData, workspace.info.face.percent, _maskInfo, _faceRect);
				}
			}
			return result;			
		}
		
		public function linkTextRect(inRect: Rectangle): Rectangle {
			inRect = inRect.clone();
			inRect.top += inRect.height * 0.8;
			inRect.inflate(-10, -10);
			return inRect;
		}
		
		public function faceMatrixCalc(a_bitmapData: BitmapData): uif_matrix {
			var mat	 		: uif_matrix = new uif_matrix();
			var _maskScale	: Number 	= scaleFromMask(a_bitmapData);
			var rect 		: Rectangle = _face.bitmapData.rect;
			var facePos		: Point = new Point(a_bitmapData.width * _wsInfo.face.pos[0] - rect.width * _maskScale / 2,
										a_bitmapData.height * _wsInfo.face.pos[1] - rect.height * _maskScale / 2);

			mat.scale(_maskScale, _maskScale);
			mat.translate(facePos.x, facePos.y);
			return mat;
		} 
		
		protected function doGetResult(e: ObjectEvent): void {
			e.object.result = new BitmapData(e.object.size.x, e.object.size.y, true, (e.object.options && e.object.options.noback)?0x00FFFFFF:null);
			paintToBitmap(e.object.result, e.object.options);
			e.stopPropagation();
		}
		
		protected function doGetResultList(e: ObjectEvent): void {
			e.object.result = bitmapsList(e.object.depthMax, e.object.size);
		}
		
		protected function doRefresh(e: Event): void {
			refresh();
		}
		
		protected function refresh(): void {
			bitmapData.fillRect(bitmapData.rect, 0xFFFFFF);
			refreshElems();
			
			if (_face) {
				repaintBitmapData(bitmapData, null);
				dispatchEvent(new Event(clt_events.AFTERREFRESH));
			}
		}
	}
}